// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "CaeVirtualDeviceDataHandlerThread.h"
#include "../cae_agent/CaeSocketAgent.h"
#include "../cae_service/CaeEngineControl.h"
extern "C" {
#include "libavformat/avformat.h"
}

const int CameraInfoLength = 3;
const int CameraSizeLength = 4;
const int MicrophoneTypeLength = 2;
const int WaitExitTime = 10;
const int FRONT_CAMERA_ORIENTATION = 270;
const int REAR_CAMERA_ORIENTATION = 90;

CaeVirtualDeviceDataHandlerThread::CaeVirtualDeviceDataHandlerThread(CaeDataPipe* pipe)
{
    m_threadStatus = CAE_THREAD_STATUS_NEW;
    m_virtualDeviceData = pipe;
    m_cameraLocalDataHandleThread = nullptr;
    m_microphoneLocalDataHandleThread = nullptr;
}

CaeVirtualDeviceDataHandlerThread::~CaeVirtualDeviceDataHandlerThread()
{
    m_virtualDeviceData->ClearPipeData();
    m_threadStatus = CAE_THREAD_STATUS_EXIT;
    if (m_cameraLocalDataHandleThread != nullptr) {
        m_cameraLocalDataHandleThread->Stop();
        while (m_cameraLocalDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_EXIT) {
            usleep(10);
        }
        delete m_cameraLocalDataHandleThread;
        m_cameraLocalDataHandleThread = nullptr;
    }
    if (m_microphoneLocalDataHandleThread != nullptr) {
        m_microphoneLocalDataHandleThread->Stop();
        while (m_microphoneLocalDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_EXIT) {
            usleep(10);
        }
        delete m_microphoneLocalDataHandleThread;
        m_microphoneLocalDataHandleThread = nullptr;
    }
    LOGI("CaeVirtualDeviceDataHandlerThread destroy ok.");
}

void CaeVirtualDeviceDataHandlerThread::Run() {
    char threadName[] = "VirtualDeviceDataHandle";
    prctl(PR_SET_NAME, threadName);
    LOGI("CaeVirtualDeviceDataHandlerThread run.");

#if ENABLE_INJECT_LOCAL_CAMERA_STREAM
    if (CaeVirtualDeviceConfig::GetLocalCameraStreamInjectFileType() == VMI_H264) {
        GetLocalH264StreamParams();
    }
#endif

    while (m_threadStatus == CAE_THREAD_STATUS_RUNNING) {
        usleep(1000);
        CaePipeData *pipeData = m_virtualDeviceData->GetPipeData();
        if (pipeData == nullptr || pipeData->GetLen() <= 0) {
            continue;
        }
        int ret = HandleDataToVirtualDevice(pipeData, pipeData->GetType());
        if (ret < 0) {
            LOGD("HandleDataToVmiDevice send failed.");
        }
    }
    m_threadStatus = CAE_THREAD_STATUS_EXIT;
    LOGI("CaeVirtualDeviceDataHandlerThread exit.");
}

bool CaeVirtualDeviceDataHandlerThread::GetLocalH264StreamParams()
{
    AVFormatContext *fmt_ctx = nullptr;
    if (avformat_open_input(&fmt_ctx,
                            CaeVirtualDeviceConfig::GetCameraStreamH264FilePath(),
                            av_find_input_format("h264"),
                            nullptr)) {
        LOGE("Open input file failed.");
        return false;
    }

    if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
        LOGE("Could not find stream information.");
        avformat_close_input(&fmt_ctx);
        return false;
    }

    int video_stream_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (video_stream_idx < 0) {
        LOGE("Could not find video stream.");
        avformat_close_input(&fmt_ctx);
        return false;
    }

    AVCodecParameters *avCodecParameters = fmt_ctx->streams[video_stream_idx]->codecpar;
    m_width = avCodecParameters->width;
    m_height = avCodecParameters->height;
    avformat_close_input(&fmt_ctx);
    return true;
}

int CaeVirtualDeviceDataHandlerThread::HandleDataToVirtualDevice(CaePipeData *pipeData, uint8_t type)
{
    int ret = 0 ;
    uint8_t  *data = const_cast<uint8_t *>(pipeData->GetBuffer()) + sizeof(STREAM_MSG_HEAD);
    uint32_t length = pipeData->GetLen() - sizeof(STREAM_MSG_HEAD);
    struct MSG_HEADER header;
    auto *rawHeader = (MSG_HEADER *) data;
    header.version = ntohs(rawHeader->version);
    header.optType = ntohs(rawHeader->optType);
    header.devType = ntohs(rawHeader->devType);
    header.devId = ntohs(rawHeader->devId);
    header.len = ntohl(rawHeader->len);
    header.nextProto = ntohs(rawHeader->nextProto);
    header.hopLimits = ntohs(rawHeader->hopLimits);

    if (type == CAE_MSG_VIRTUAL_CAMERA) {
        int parseLen = 0;
        while(parseLen + sizeof(MSG_HEADER) <= length) {
            HandleCameraMsg(header, data, pipeData);
            parseLen += header.len;
            data += parseLen;
        }
    } else if (type == CAE_MSG_VIRTUAL_MICROPHONE) {
        int parseLen = 0;
        while(parseLen + sizeof(MSG_HEADER) <= length) {
            HandleMicrophoneMsg(header, data, pipeData);
            parseLen += header.len;
            data += parseLen;
        }
    } else {
        if(CaeSocketAgent::GetInstance().Send((void *) pipeData->GetBuffer(), pipeData->GetLen()) < 0) {
            LOGD("Failed to send virtual device data.");
        }
    }
    return 0;
}

void CaeVirtualDeviceDataHandlerThread::HandleCameraMsg(MSG_HEADER header, uint8_t *data, CaePipeData *pipeData) {
    switch (header.optType) {
        case OPT_CAMERA_GET_PARAM_REQ:
            LOGI("Camera cmd type : get param.");
            HandleGetParamReq(&header);
            break;
        case OPT_CAMERA_SET_PARAM_REQ:
            LOGI("Camera cmd type : set param.");
            HandleCameraSetParamReq(&header, data + sizeof(MSG_HEADER));
            break;
        case OPT_CAMERA_GET_INFO_REQ:
            LOGI("Camera cmd type : get info.");
            HandleGetInfoReq(&header);
            break;
        case OPT_CAMERA_START_PREVIEW_REQ:
            LOGI("Camera cmd type : start preview.");
            HandleStartPreviewReq(&header, data + sizeof(MSG_HEADER), pipeData);
            break;
        case OPT_CAMERA_STOP_PREVIEW_REQ:
            LOGI("Camera cmd type : stop preview.");
            HandleStopPreviewReq(&header, pipeData);
            break;
        case OPT_CAMERA_IS_USE_H264_REQ:
            LOGI("Camera cmd type : use h264.");
            break;
        case OPT_CAMERA_GET_SIZE_REQ:
            LOGI("Camera cmd type : get size.");
            HandleGetSizeReq(&header);
            break;
        default:
            LOGE("Camera cmd type : type error.");
            break;
    }
}

void CaeVirtualDeviceDataHandlerThread::HandleMicrophoneMsg(MSG_HEADER header, uint8_t *data, CaePipeData *pipeData) {
    switch (header.optType) {
        case OPT_MIC_SET_PARAM_REQ:
            LOGI("Microphone cmd type : set params.");
            HandleMicrophoneSetParamReq(&header, data + sizeof(MSG_HEADER), pipeData);
            break;
        case OPT_MIC_START_RECORD_REQ:
            LOGI("Microphone cmd type : start record.");
            HandleStartRecordReq(&header, data + sizeof(MSG_HEADER), pipeData);
            break;
        case OPT_MIC_STOP_RECORD_REQ:
            LOGI("Microphone cmd type : stop record.");
            HandleStopRecordReq(&header, data + sizeof(MSG_HEADER), pipeData);
            break;
        default:
            LOGE("Microphone cmd type : type error.");
            break;

    }
}

void CaeVirtualDeviceDataHandlerThread::HandleGetInfoReq(MSG_HEADER *header)
{
    struct RSP_MSG_HEADER rspHeader{};
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_CAMERA_GET_INFO_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER) + CameraInfoLength);
    uint8_t buf[CameraInfoLength] = {0};

    auto *orientation = (uint16_t *) &(buf[1]);
    if(header->devId == 0) {
        buf[0] = 0x0;
        *orientation = htons(REAR_CAMERA_ORIENTATION);
    } else {
        buf[0] = 0x1;
        *orientation = htons(FRONT_CAMERA_ORIENTATION);
    }

#if ENABLE_INJECT_LOCAL_CAMERA_STREAM
    if(CaeVirtualDeviceConfig::GetLocalCameraStreamInjectFileType() == VMI_H264) {
        if (m_width < m_height) {
            *orientation = htons(0);
        }
    }
#endif
    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));
    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, buf, CameraInfoLength);
}

void CaeVirtualDeviceDataHandlerThread::HandleGetParamReq(MSG_HEADER *header)
{
    int length = CAMERA_PARAMETERS.length() + 1; // \0
    struct RSP_MSG_HEADER rspHeader;
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_CAMERA_GET_PARAM_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER) + length);

    char *buf = (char*) (new char[length]);
    (void) memset_s(buf, length, 0, length);
    (void) strncpy_s(buf, length, CAMERA_PARAMETERS.c_str(), length - 1);

    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));
    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, (uint8_t*)buf, length);
}

void CaeVirtualDeviceDataHandlerThread::HandleCameraSetParamReq(MSG_HEADER *header, uint8_t *body)
{
    struct RSP_MSG_HEADER rspHeader;
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_CAMERA_SET_PARAM_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER));
    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));
}

void CaeVirtualDeviceDataHandlerThread::HandleStartPreviewReq(MSG_HEADER *header, uint8_t *body, CaePipeData *pipeData)
{
    CaeVirtualDeviceConfig::SetOpenCameraFlag(true, header, body, pipeData);
    int width = ntohs(*((uint16_t*)body));
    int height = ntohs(*((uint16_t*)(body + 2)));
    LOGI("Width = %d, height = %d, the request width = %d, height = %d.", m_width, m_height, width, height);

    struct RSP_MSG_HEADER rspHeader;
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_CAMERA_START_PREVIEW_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER));
    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));

#if !ENABLE_INJECT_LOCAL_CAMERA_STREAM
        if (CaeSocketAgent::GetInstance().Send((void *)pipeData->GetBuffer(), pipeData->GetLen()) < 0) {
            LOGD("Failed to send camera start preview msg.");
        }
        return;
#endif
    if (m_cameraLocalDataHandleThread != nullptr) {
        LOGE("Already has m_cameraLocalDataHandleThread.");
        return;
    }

    m_cameraLocalDataHandleThread = new(std::nothrow) CaeLocalDataHandleThread(rspHeader.version
            , rspHeader.devType, rspHeader.devId, width, height
            , CaeVirtualDeviceConfig::GetLocalCameraStreamInjectFileType());
    if (m_cameraLocalDataHandleThread == nullptr) {
        LOGE("Failed to create m_cameraLocalDataHandleThread.");
        return;
    }
    if (m_cameraLocalDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_RUNNING) {
        m_cameraLocalDataHandleThread->Start();
        LOGI("Start m_cameraLocalDataHandleThread.");
    }
}

void CaeVirtualDeviceDataHandlerThread::HandleStopPreviewReq(MSG_HEADER *header, CaePipeData *pipeData)
{

    CaeVirtualDeviceConfig::SetOpenCameraFlag(false, nullptr, nullptr, nullptr);
#if !ENABLE_INJECT_LOCAL_CAMERA_STREAM
    if (CaeSocketAgent::GetInstance().Send((void *)pipeData->GetBuffer(), pipeData->GetLen()) < 0) {
        LOGD("Failed to send camera stop preview msg.");
    }
    return;
#endif
    struct RSP_MSG_HEADER rspHeader;
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_CAMERA_STOP_PREVIEW_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER));
    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));
    if (m_cameraLocalDataHandleThread == nullptr) {
        LOGE("Already delete m_cameraLocalDataHandleThread.");
        return;
    }
    m_cameraLocalDataHandleThread->Stop();
    while (m_cameraLocalDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_EXIT) {
        usleep(WaitExitTime);
    }
    delete m_cameraLocalDataHandleThread;
    m_cameraLocalDataHandleThread = nullptr;
    LOGI("m_cameraLocalDataHandleThread exited.");
}

void CaeVirtualDeviceDataHandlerThread::HandleGetSizeReq(MSG_HEADER *header)
{
    struct RSP_MSG_HEADER rspHeader;
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_CAMERA_GET_SIZE_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER) + CameraSizeLength);
    
    uint8_t buf[CameraSizeLength] = {0};
    uint16_t *ptr = (uint16_t *)buf;
    int width = -1, height = -1;
#if ENABLE_INJECT_LOCAL_CAMERA_STREAM
    if(CaeVirtualDeviceConfig::GetLocalCameraStreamInjectFileType() == VMI_H264) {
        width = m_width;
        height = m_height;
    }
#endif
    *ptr = htons(width);
    *(ptr + 1) = htons(height);
    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));
    CaeEngineControl::GetInstance().VmiDeviceSend(CAMERA, buf, CameraSizeLength);
}

void CaeVirtualDeviceDataHandlerThread::HandleMicrophoneSetParamReq(MSG_HEADER *header, uint8_t *body, CaePipeData *pipeData)
{
#if !ENABLE_INJECT_LOCAL_MICROPHONE_STREAM
    if (CaeSocketAgent::GetInstance().Send((void *)pipeData->GetBuffer(), pipeData->GetLen()) < 0) {
        LOGD("Failed to send microphone set param msg.");
    }
    return;
#endif
    struct RSP_MSG_HEADER rspHeader{};
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_MIC_SET_PARAM_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER));

    auto *ptr = (uint16_t *)body;
    uint16_t maxFrameSize = ntohs(*ptr ++);
    uint16_t format = ntohs(*ptr ++);
    m_sampleRate = ntohs(*ptr ++);
    uint16_t channel = ntohs(*ptr ++);
    LOGI("Microphone param : maxFrameSize = %d, format = %d, sampleRate = %d, channel = %d", maxFrameSize, format, m_sampleRate, channel);

    CaeEngineControl::GetInstance().VmiDeviceSend(MICROPHONE, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));
}

void CaeVirtualDeviceDataHandlerThread::HandleStartRecordReq(MSG_HEADER *header, uint8_t *body, CaePipeData *pipeData)
{
    CaeVirtualDeviceConfig::SetOpenMicrophoneFlag(true, header, body, pipeData);
#if !ENABLE_INJECT_LOCAL_MICROPHONE_STREAM
    if (CaeSocketAgent::GetInstance().Send((void *)pipeData->GetBuffer(), pipeData->GetLen()) < 0) {
        LOGD("Failed to send microphone start record msg.");
    }
    return;
#endif
    struct RSP_MSG_HEADER rspHeader;
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_MIC_START_RECORD_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER) + MicrophoneTypeLength);

    uint8_t buf[MicrophoneTypeLength] = {0};
    uint16_t *ptr = (uint16_t *)buf;
    *ptr = htons((ENABLE_INJECT_LOCAL_MICROPHONE_STREAM ? VMI_PCM : VMI_OPUS) - VMI_PCM);
    CaeEngineControl::GetInstance().VmiDeviceSend(MICROPHONE, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));
    CaeEngineControl::GetInstance().VmiDeviceSend(MICROPHONE, buf, MicrophoneTypeLength);

    if (m_microphoneLocalDataHandleThread != nullptr) {
        LOGE("Already has m_microphoneLocalDataHandleThread.");
        return;
    }

    m_microphoneLocalDataHandleThread = new(std::nothrow) CaeLocalDataHandleThread(rspHeader.version
            , rspHeader.devType, rspHeader.devId, m_sampleRate
            , CaeVirtualDeviceConfig::GetLocalMicrophoneStreamInjectFileType());
    if (m_microphoneLocalDataHandleThread == nullptr) {
        LOGE("Failed to create m_microphoneLocalDataHandleThread.");
        return;
    }
    if (m_microphoneLocalDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_RUNNING) {
        m_microphoneLocalDataHandleThread->Start();
        LOGI("Start m_microphoneLocalDataHandleThread.");
    }
}

void CaeVirtualDeviceDataHandlerThread::HandleStopRecordReq(MSG_HEADER *header, uint8_t *body, CaePipeData *pipeData)
{
    CaeVirtualDeviceConfig::SetOpenMicrophoneFlag(false, nullptr, nullptr, nullptr);
#if !ENABLE_INJECT_LOCAL_MICROPHONE_STREAM
        if (CaeSocketAgent::GetInstance().Send((void *)pipeData->GetBuffer(), pipeData->GetLen()) < 0) {
            LOGD("Failed to send microphone stop record msg.");
        }
        return;
#endif
    struct RSP_MSG_HEADER rspHeader;
    rspHeader.version = htons(header->version);
    rspHeader.optType = htons(OPT_MIC_STOP_RECORD_RSP);
    rspHeader.devType = htons(header->devType);
    rspHeader.devId = htons(header->devId);
    rspHeader.len = htonl(sizeof(RSP_MSG_HEADER));
    CaeEngineControl::GetInstance().VmiDeviceSend(MICROPHONE, (uint8_t*)&rspHeader, sizeof(RSP_MSG_HEADER));
    if (m_microphoneLocalDataHandleThread == nullptr) {
        LOGE("Already delete m_microphoneLocalDataHandleThread.");
        return;
    }
    m_microphoneLocalDataHandleThread->Stop();
    while (m_microphoneLocalDataHandleThread->m_threadStatus != CAE_THREAD_STATUS_EXIT) {
        usleep(WaitExitTime);
    }
    delete m_microphoneLocalDataHandleThread;
    m_microphoneLocalDataHandleThread = nullptr;
    LOGI("m_microphoneLocalDataHandleThread exited.");
}